package com.jianggujin.zkgui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeUnit;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.CuratorFrameworkFactory.Builder;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
import org.apache.curator.utils.ZKPaths;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

public class MainFrame extends JFrame {
    private final static String TITLE = "zookeeper可视化工具";
    private CuratorFramework client;
    private ConfigItem configItem;
    // 图标
    private ImageIcon logoIcon;
    private ImageIcon connectIcon;
    private ImageIcon disconnectIcon;

    // 工具栏组件
    private JButton connectBtn;
    private JButton disconnectBtn;

    private JSplitPane centerPane;
    // 中部左侧组件
    private JTree nodeTree;
    private DefaultTreeModel nodeTreeModel;
    private DefaultMutableTreeNode rootNode;
    private JPopupMenu nodeTreeMenu;
    private JMenuItem nodeTreeAddMenu;
    private JMenuItem nodeTreeDeleteMenu;

    // 中部右侧
    private NodeValuePanel nodeValuePanel;

    private JTextArea logArea;
    private JButton clearLog;

    private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() {
        @Override
        public void stateChanged(CuratorFramework client, ConnectionState newState) {
            handleStateChange(newState);
        }
    };
    private final TreeCacheListener treeCacheListener = new TreeCacheListener() {
        @Override
        public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
            handleChildEvent(event);
        }
    };

    private TreeCache cache = null;

    /**
     * 初始化图标
     */
    private void initIcon() {
        logoIcon = new ImageIcon(getClass().getResource("/icon/logo.png"));
        connectIcon = new ImageIcon(getClass().getResource("/icon/connect.png"));
        disconnectIcon = new ImageIcon(getClass().getResource("/icon/disconnect.png"));
    }

    private void handleStateChange(ConnectionState newState) {
        switch (newState) {
        case CONNECTED:
            connect();
            break;
        case LOST:
            disconnect();
            break;
        case RECONNECTED:
            clearNodeTree();
            connect();
            break;
        default:
            break;
        }
    }

    private void handleChildEvent(TreeCacheEvent event) {
        appendLog(event);
        switch (event.getType()) {
        case NODE_ADDED:
            addNode(event.getData().getPath());
            break;
        case NODE_REMOVED:
            removeNode(event.getData().getPath());
            break;
        case NODE_UPDATED:
            if (event.getData().getPath().equals(nodeValuePanel.getPath())) {
                showPathInfo(nodeValuePanel.getPath());
            }
            break;
        default:
            break;
        }
    }

    private synchronized void addNode(String path) {
        String[] paths = path.split("/");
        int len = paths.length;
        if (len == 0) {// 根路径
            rootNode.add(new DefaultMutableTreeNode("/"));
            reloadNodeTree(rootNode);
        } else {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) rootNode.getChildAt(0);
            for (int i = 1; i < len - 1; i++) {
                int count = node.getChildCount();
                for (int j = 0; j < count; j++) {
                    DefaultMutableTreeNode tmp = (DefaultMutableTreeNode) node.getChildAt(j);
                    if (paths[i].equals(tmp.getUserObject())) {
                        node = tmp;
                        break;
                    }
                }
            }
            node.add(new DefaultMutableTreeNode(paths[len - 1]));
            reloadNodeTree(node);
        }
    }

    private synchronized void removeNode(String path) {
        String[] paths = path.split("/");
        int len = paths.length;
        if (len == 0) {// 根路径
            rootNode.removeAllChildren();
            reloadNodeTree(rootNode);
        } else {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) rootNode.getChildAt(0);
            for (int i = 1; i < len; i++) {
                int count = node.getChildCount();
                for (int j = 0; j < count; j++) {
                    DefaultMutableTreeNode tmp = (DefaultMutableTreeNode) node.getChildAt(j);
                    if (paths[i].equals(tmp.getUserObject())) {
                        node = tmp;
                        break;
                    }
                }
            }
            DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
            node.removeFromParent();
            reloadNodeTree(parent);
        }
    }

    /**
     * 初始化组件
     */
    private void initComponent() {
        initToolBar();
        initCenterComponent();
        initBottomComponent();
    }

    /**
     * 初始化工具栏
     */
    private void initToolBar() {
        JToolBar toolBar = new JToolBar();
        toolBar.setFloatable(false);

        connectBtn = new JButton(connectIcon);
        connectBtn.setToolTipText("连接");
        connectBtn.addActionListener(e -> ConfigItemChooserDialog.showChooser(this));
        toolBar.add(connectBtn);

        disconnectBtn = new JButton(disconnectIcon);
        disconnectBtn.setToolTipText("断开连接");
        disconnectBtn.addActionListener(e -> disconnect());
        toolBar.add(disconnectBtn);

        this.add(toolBar, BorderLayout.NORTH);
    }

    /**
     * 初始化中间部分组件
     */
    private void initCenterComponent() {
        rootNode = new DefaultMutableTreeNode("");
        nodeTreeModel = new DefaultTreeModel(rootNode);
        nodeTree = new JTree(nodeTreeModel);
        nodeTree.setRootVisible(false);
        nodeTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        nodeTree.addTreeSelectionListener(e -> {
            TreePath treePath = e.getNewLeadSelectionPath();
            if (treePath == null) {
                return;
            }
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
            if (treeNode == null) {
                return;
            }
            String path = buildPath(treeNode);
            showPathInfo(path);
        });

        nodeTreeMenu = new JPopupMenu();
        nodeTreeAddMenu = new JMenuItem("添加节点");
        ActionListener actionListener = e -> {
            TreePath treePath = nodeTree.getSelectionPath();
            if (treePath == null) {
                return;
            }
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
            if (treeNode == null) {
                return;
            }
            String path = buildPath(treeNode);
            if (e.getSource() == nodeTreeAddMenu) {
                 AddPathDialog.showDialog(this, path);
            } else {
                if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this, "确定删除节点[" + path + "]?", "确认",
                        JOptionPane.YES_NO_OPTION)) {
                    try {
                        client.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);
                    } catch (Exception ex) {
                        appendLog(ex);
                    }
                }
            }
        };
        nodeTreeAddMenu.addActionListener(actionListener);
        nodeTreeDeleteMenu = new JMenuItem("删除节点");
        nodeTreeDeleteMenu.addActionListener(actionListener);
        nodeTreeMenu.add(nodeTreeAddMenu);
        nodeTreeMenu.add(nodeTreeDeleteMenu);

        nodeTree.addMouseListener(new MouseAdapter() {
            private void processPopTrigger(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    TreePath path = nodeTree.getPathForLocation(e.getX(), e.getY());
                    if (path == null) {
                        return;
                    }
                    nodeTree.setSelectionPath(path);
                    nodeTreeDeleteMenu.setVisible(path.getParentPath() != null);
                    nodeTreeMenu.show(nodeTree, e.getX(), e.getY());
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {
                processPopTrigger(e);
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                processPopTrigger(e);
            }
        });

        nodeValuePanel = new NodeValuePanel(this);
        nodeValuePanel.setDividerLocation(150);
        nodeValuePanel.setOneTouchExpandable(true);

        JScrollPane nodeTreeRoot = new JScrollPane(nodeTree);
        nodeTreeRoot.setBorder(BorderFactory.createTitledBorder("节点"));

        centerPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, nodeTreeRoot, nodeValuePanel);
        centerPane.setOneTouchExpandable(true);
        centerPane.setDividerLocation(150);
        this.add(centerPane);
    }

    /**
     * 初始化底部组件
     */
    private void initBottomComponent() {
        JPanel buttom = new JPanel(new BorderLayout());
        logArea = new JTextArea();
        logArea.setLineWrap(true);
        logArea.setBackground(Color.black);
        logArea.setForeground(Color.white);
        logArea.setRows(8);
        logArea.setEditable(false);

        buttom.add(new JScrollPane(logArea));

        clearLog = new JButton("清空");
        clearLog.addActionListener(e -> logArea.setText(""));
        buttom.add(clearLog, BorderLayout.EAST);

        this.add(buttom, BorderLayout.SOUTH);
    }

    private void showPathInfo(String path) {
        try {
            byte[] data = client.getData().storingStatIn(new Stat()).forPath(path);
            nodeValuePanel.showNodeInfo(path, data);
        } catch (Exception e) {
            appendLog(e);
        }
    }

    /**
     * 初始化窗体
     */
    private void initFrame() {
        setTitle(TITLE);
        setIconImage(logoIcon.getImage());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(600, 550);
        setLocationRelativeTo(null);
    }

    /**
     * 连接
     */
    private void connect() {
        this.connectBtn.setEnabled(false);
        this.disconnectBtn.setEnabled(true);
    }

    /**
     * 断开连接
     */
    private void disconnect() {
        setTitle(TITLE);
        this.connectBtn.setEnabled(true);
        this.disconnectBtn.setEnabled(false);
        if (cache != null) {
            CloseableUtils.closeQuietly(cache);
            cache = null;
        }
        if (client != null) {
            CloseableUtils.closeQuietly(client);
            client = null;
        }
        this.configItem = null;
        clearNodeTree();
    }

    public MainFrame() {
        initIcon();
        initComponent();
        disconnect();
        initFrame();
    }

    /**
     * 连接
     * 
     * @param configItem
     */
    public void connect(ConfigItem configItem) {
        this.configItem = configItem;
        if (configItem.getNamespace() != null && configItem.getNamespace().length() > 0) {
            setTitle(configItem.getName() + "(" + configItem.getConnectString() + ") - " + configItem.getNamespace());
        } else {
            setTitle(configItem.getName() + "(" + configItem.getConnectString() + ")");
        }
        client = initZkClient();
        client.getConnectionStateListenable().addListener(connectionStateListener);
        client.start();
        try {
            client.blockUntilConnected(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            appendLog(e);
        }
        cache = new TreeCache(client, "/");
        cache.getListenable().addListener(treeCacheListener);
        try {
            cache.start();
        } catch (Exception e) {
            disconnect();
            appendLog(e);
            return;
        }
        if (!isConnected()) {
            disconnect();
            JOptionPane.showMessageDialog(this, "连接失败：" + configItem.getConnectString(), "错误",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    private CuratorFramework initZkClient() {
        Builder builder = CuratorFrameworkFactory.builder();
        builder.connectString(configItem.getConnectString());
        String namespace = configItem.getNamespace();
        if (namespace != null && namespace.length() > 0) {
            builder.namespace(namespace);
        }
        builder.connectionTimeoutMs(5000);
        ExponentialBackoffRetry retry = new ExponentialBackoffRetry(1000, 3);
        builder.retryPolicy(retry);
        return builder.build();
    }

    private boolean isConnected() {
        return client != null && client.getZookeeperClient().isConnected();
    }

    private void reloadNodeTree(TreeNode node) {
        if (node != null) {
            nodeTreeModel.reload(node);
        } else {
            nodeTreeModel.reload(rootNode);
        }
        centerPane.setDividerLocation(centerPane.getDividerLocation());
    }

    /**
     * 清空节点
     */
    private synchronized void clearNodeTree() {
        rootNode.removeAllChildren();
        reloadNodeTree(rootNode);
        nodeValuePanel.clear();
    }

    /**
     * 通过节点创建ZK路径
     * 
     * @param node
     * @return
     */
    private String buildPath(TreeNode node) {
        TreeNode[] nodes = ((DefaultMutableTreeNode) node).getPath();
        StringBuilder builder = new StringBuilder();
        for (TreeNode item : nodes) {
            builder.append("/").append(item.toString());
        }
        return builder.toString().replaceAll("/+", "/");
    }

    public void saveNodeValue(String path, String data) {
        if (client != null) {
            try {
                client.setData().forPath(path, data == null ? null : data.getBytes("UTF-8"));
            } catch (Exception e) {
                appendLog(e);
            }
        }
    }

    public void addPath(String path, String childPath, String childValue, CreateMode createMode) {
        if (client != null) {
            try {
                client.create().creatingParentContainersIfNeeded().withMode(createMode).forPath(
                        ZKPaths.makePath(path, childPath), childValue == null ? null : childValue.getBytes("UTF-8"));
            } catch (Exception e) {
                appendLog(e);
            }
        }
    }

    private void appendLog(Exception e) {
        try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw);) {
            e.printStackTrace(pw);
            this.logArea.append(sw.toString());
            this.logArea.append("\r\n");
        } catch (Exception ex) {
            this.logArea.append(ex.toString());
            this.logArea.append("\r\n");
        }
    }

    private void appendLog(TreeCacheEvent event) {
        switch (event.getType()) {
        case CONNECTION_LOST:
            this.logArea.append("连接丢失...\r\n");
            break;
        case CONNECTION_RECONNECTED:
            this.logArea.append("重新连接...\r\n");
            break;
        case CONNECTION_SUSPENDED:
            this.logArea.append("连接挂起...\r\n");
            break;
        case INITIALIZED:
            this.logArea.append("初始化完成...\r\n");
            break;
        case NODE_ADDED:
            this.logArea.append("添加节点，节点路径：" + event.getData().getPath() + "，节点值："
                    + convertData(event.getData().getData()) + "\r\n");
            break;
        case NODE_REMOVED:
            this.logArea.append("删除节点，节点路径：" + event.getData().getPath() + "，节点值："
                    + convertData(event.getData().getData()) + "\r\n");
            break;
        case NODE_UPDATED:
            this.logArea.append("修改节点，节点路径：" + event.getData().getPath() + "，节点值："
                    + convertData(event.getData().getData()) + "\r\n");
            break;
        default:
            break;
        }
    }

    protected static String convertData(byte[] data) {
        if (data == null) {
            return null;
        }
        try {
            return new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(NimbusLookAndFeel.class.getName());
            } catch (Exception e) {
            }
            new MainFrame().setVisible(true);
        });
    }
}
